/**
 * 
 */
package com.alstudio.module.xmpp.EventHandler.network;

import android.os.Handler;
import android.os.Message;

import com.alstudio.module.xmpp.ALXmppEventCallback;
import com.alstudio.module.xmpp.distributor.distributor.CheckDistributorEventCallback;
import com.alstudio.module.xmpp.protocol.xmpp.ALXmppAdapter;
import com.alstudio.module.xmpp.protocol.xmpp.ALXmppException;
import com.alstudio.module.xmpp.protocol.xmpp.data.ALXmppConnectionState;
import com.alstudio.module.xmpp.protocol.xmpp.data.ALXmppData;
import com.alstudio.module.xmpp.request.ALUiXmppRequestManager;
import com.alstudio.pojo.ALXmppEvent;
import com.alstudio.utils.account.ALAccount;
import com.alstudio.utils.android.net.ALConnectionManager.ALConnectionType;
import com.alstudio.utils.log.ALLog;

/**
 * 
 * 实现XMPP登陆、注册方法
 * 
 * @author alonso lee
 * 
 */
public abstract class BaseALXmppLoginWrapper implements ALXmppEventCallback,
		CheckDistributorEventCallback {

	protected ALXmppAdapter mDefaultXmppAdapter;

	protected ALUiXmppRequestManager mALUiXmppRequestManager = new ALUiXmppRequestManager();

	// 检测当前是否没有网络
	protected boolean isNoNetworkConnection = true;
	// 初步定连接服务器超时的时间为30S
	protected final int LOGIN_IM_SERVER_TIME_OUT = 30 * 1000;
	// 掉线重练的延时时间为3s
	protected final int RECONNECT_PERIOD = 3 * 1000;

	// 创建登录IM服务器所需的数据结构
	protected ALXmppData xmppLoginData;

	// 账号信息
	protected ALAccount localAccount;

	// 当前登录服务器已经超时，需要重新登录
	protected final int RECONNECT_TO_IM_SERVER = 1000;

	// 发送c2s心跳包
	private final int SEND_C2S_PING_REQUEST_TO_SERVER = 16;

	// 手机IMEI
	protected String imei;

	/* 此变量用于标识当前是否无可用网络 */
	protected boolean isC2TreadRunning = false;

	/* 此线程用于登录xmpp服务器后发送心跳包 */
	private c2sPingThread c2sThread = new c2sPingThread();

	// 当前xmpp连接的会话id
	protected String curXmppSessionId;

	protected boolean isLoginWithId = false;

	/**
	 * 注册XMPP中间件
	 * 
	 * @param adapter
	 */
	public abstract void registerALXmppAdapter(ALXmppAdapter adapter);

	/**
	 * 处理网络连接建立事件，在这里只想登陆xmpp操作
	 * 
	 * @param type
	 */
	public abstract void invokeDataConnectionConnected(ALConnectionType type);

	/**
	 * 处理网络连接丢失事件
	 */
	public abstract void invokeDataConnectionLoss();

	/**
	 * 处理C2S心跳线程的掉线重连事件
	 */
	protected abstract void invokeC2SThreadReconnectRequest();

	/**
	 * 执行普通登陆事件
	 * 
	 * @param username
	 * @param pwd
	 * @param authType
	 */
	protected abstract void invokeUIConnectXmppRequest(String username,
			String pwd, boolean authType);

	/**
	 * 执行登陆
	 * 
	 * @param isSystemType
	 * @param user
	 * @param pwd
	 * @param callingName
	 * @param loginNow
	 */
	protected abstract void invokeReconnect(boolean isSystemType, String user,
			String pwd, String callingName, boolean loginNow);

	/**
	 * 处理掉线事件
	 * 
	 * @param data
	 */
	protected abstract void invokeConnectionLost(ALXmppEvent data);

	/**
	 * 处理连接成功事件
	 * 
	 * @param data
	 */
	protected abstract void invokeConnectionEstablished(ALXmppEvent data);

	/**
	 * 处理账号验证失败事件
	 * 
	 * @param data
	 */
	protected abstract void invokeConnectionAuthFailed(ALXmppEvent data);

	/**
	 * 分发XMPP事件到处理函数
	 * 
	 * @param data
	 */
	protected abstract void dispatchXmppEvent(ALXmppEvent data);

	/**
	 * 处理更新XMPP连接的id
	 * 
	 * @param data
	 */
	protected abstract void invokeUpdateXmppSessionId(ALXmppEvent data);

	/**
	 * 清除占用资源
	 */
	public abstract void release();

	/**
	 * 处理UI请求事件
	 * 
	 * @param request
	 */
	public abstract void invokeUIRequest(Object request);

	/**
	 * 处理接收到服务器响应C2S信息事件
	 */
	protected void invokeC2sResponseEvent() {
		mReconnectHandler.removeMessages(SEND_C2S_PING_REQUEST_TO_SERVER);
	}

	/**
	 * 开启C2S心跳包线程
	 */
	protected void invokeStartC2sPing() {
		if (isC2TreadRunning == false) {
			// 启动心跳线程
			c2sThread.setName("C2sPing Thread");
			c2sThread.start();
		} else {
			invokeC2sResponseEvent();
			ALLog.d("c2s线程已开启！");
		}
	}

	/**
	 * 发送重连服务器请求。
	 * 
	 * @param loginNow
	 *            true表示需要马上重连，false表示要判断是否需要加入等待队列
	 * @author Alonso Lee
	 */
	protected synchronized void sendReconnectRequest(final boolean loginNow) {
		new Thread() {
			@Override
			public void run() {
				// 休眠3S
				try {
					Thread.sleep(RECONNECT_PERIOD);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				cleanReconnectRequest();
				if (!loginNow) {
					if (mDefaultXmppAdapter.xmppCheckLoginState() != ALXmppConnectionState.XMPP_STATE_CONNECTION_ESTABLISHED) {
						invokeReconnect(true, localAccount.getUserName(),
								localAccount.getPassWord(),
								"sendReconnectRequest", loginNow);
					} else {
						// 已经连接成功，不用再练了
						ALLog.d("已经连接成功，不用再连了 ");
					}
				} else {
					// 直接登陆去啊
					invokeReconnect(true, localAccount.getUserName(),
							localAccount.getPassWord(), "sendReconnectRequest",
							loginNow);
				}

			};
		}.start();
	}

	/**
	 * 处理C2S、登陆超时事件
	 */
	protected Handler mReconnectHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case SEND_C2S_PING_REQUEST_TO_SERVER:
				int nextTimeout = msg.arg1;
				if (nextTimeout == 0) {
					// 重连
					ALLog.d("1分45S内服务器没有响应C2S心跳包，10S后开始重连！");
					invokeC2SThreadReconnectRequest();
				} else {
					ALLog.d("再次发送心跳包！下一次超时时间为:" + nextTimeout);
					// mDefaultXmppAdapter.xmppSendC2SPingRequest(getLac(),
					// getCellid(), lat,
					// lng, wifiBssid);
					mDefaultXmppAdapter.xmppSendPingRequest();
					Message msg1 = obtainMessage(SEND_C2S_PING_REQUEST_TO_SERVER);
					if (nextTimeout == 30) {
						msg1.arg1 = 15;
					} else {
						msg1.arg1 = 0;
					}
					sendMessageDelayed(msg1, nextTimeout * 1000);
				}
				break;
			case RECONNECT_TO_IM_SERVER:
				// 当前登录服务器的连接已经超时了，重新连接吧
				ALLog.d("连接已经超时！即将开始下一次连接");
				cleanReconnectRequest();
				sendReconnectRequest(true);
				break;
			}
		};
	};

	/**
	 * 检测ALXmppAdapter是否可用
	 */
	protected void isXmppAdapterValidate() {
		if (mDefaultXmppAdapter == null) {
			throw new ALXmppException("xmpp 中间件未初始化！请首先创建ALXmppAdapter实例");
		}
	}

	/* 此常量用于标示C2S心跳包发送间隔时长 */
	private final long KEEP_ALIVE_INTERVAL = 1000 * 60 * 3; // 3 分钟
	private final long WAIT_FOR_SEVER_PING_RESPONSE = 1000 * 60; // 等待服务器响应心跳包的时长为1M

	/**
	 * c2s心跳包发送线程
	 * 
	 * @author alonso lee
	 * 
	 */
	protected class c2sPingThread extends Thread {
		@Override
		public void run() {
			// 只要网络有效，那么一直运行下去
			ALLog.d("C2S心跳线程已开启");
			isC2TreadRunning = true;
			while (true) {
				ALLog.d("循环开始");

				// 休眠3分钟后发c2s心跳包
				try {
					Thread.sleep(KEEP_ALIVE_INTERVAL);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				ALLog.d("3分钟已到，开始执行C2S Ping包检测");

				if (localAccount.isLogoff()) {
					ALLog.d("已经注销了");
					continue;
				}

				// 如果当前已经登录到服务器，那么发送C2S心跳包
				if (mDefaultXmppAdapter.xmppCheckLoginState() == ALXmppConnectionState.XMPP_STATE_CONNECTION_ESTABLISHED) {
					// 发送c2s心跳包
					mDefaultXmppAdapter.xmppSendPingRequest();

					Message msg = mReconnectHandler
							.obtainMessage(SEND_C2S_PING_REQUEST_TO_SERVER);
					// 下一次超时时间
					msg.arg1 = 30;
					// 60S后检查是否已经超时
					mReconnectHandler.sendMessageDelayed(msg,
							WAIT_FOR_SEVER_PING_RESPONSE);
				} else {
					ALLog.d("当前没有登录服务器,开始重新登录服务器！");
					invokeC2SThreadReconnectRequest();
				}
			}
		}
	}

	/**
	 * 登录成功后清除挂起的重连请求
	 * 
	 * @author Alonso Lee
	 */
	protected void cleanReconnectRequest() {
		// 清除handler中关于重连的操作
		mReconnectHandler.removeMessages(RECONNECT_TO_IM_SERVER);
	}

}
